/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithms;

import java.util.ArrayList;
import util.Constants;
import mopso.MetricsReport;
import mopso.PSO;
import mopso.Particle_CSS;
import mopso.PerformanceMetrics;
import mopso.PlotManager;
import mopso.Problem;
import mopso.Repository;
import util.Turbulence;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.axis.NumberAxis;

/**
 *
 * @author robson
 */
public class CSS_MOPSO {

    private ArrayList<Particle_CSS> swarm;
    private ArrayList<Particle_CSS> externalArchive;
    private double[] datumPoint;
    private Problem problem;
    private int nParticles;
    private int nMaxParticlesExternalArchive;
    private int currentIteration;
    private int currentNCallObjectiveFunction;
    private int beforeNCallObjectiveFunction;
    private double inertia;
    private int maxPbests;
    private int maxExternalArchive;
    private double standardDeviationMutationGaussian;
    private Particle_CSS auxParticleCurrentPbest;
    private Particle_CSS auxParticle2;
    private Particle_CSS auxParticle3;
    private Repository repositoryOperators;
    private Turbulence turbulenceOperators;
    private PSO psoOperators;
    private PlotManager plotManager;
    private PerformanceMetrics perfomanceMetrics;
    private double hypervolume;
    private double spacing;
    private double maximumSpread;
    private double timeDuration;
    private long startTimeInMs;
    private long elapsedTimeInMs;

    public CSS_MOPSO(Problem problem) {

        this.repositoryOperators = new Repository();
        this.turbulenceOperators = new Turbulence();
        this.psoOperators = new PSO();
        this.plotManager = new PlotManager();
        this.plotManager.setupPlotExternalArchive();
        this.plotManager.setupPlotSwarm();

        this.plotManager.setDebug(false);
        this.perfomanceMetrics = new PerformanceMetrics();

        this.swarm = new ArrayList<Particle_CSS>();
        this.externalArchive = new ArrayList<Particle_CSS>();

        this.datumPoint = new double[problem.getNObjectives()];

        this.problem = problem;
        this.nParticles = Constants.SWARMSIZE;
        this.nMaxParticlesExternalArchive = Constants.EXTERNALARCHIVESIZE;
        this.inertia = Constants.MAXINERTIA;
        this.standardDeviationMutationGaussian = Constants.INITIALSDMUTATIONGAUSSIAN;

        this.auxParticleCurrentPbest = new Particle_CSS(problem);
        this.auxParticle2 = new Particle_CSS(problem);
        this.auxParticle3 = new Particle_CSS(problem);

        System.out.println("\nTécnica: CSS-MOPSO");
        System.out.println("Problema: " + problem.getName());
        
        startTimeInMs = System.currentTimeMillis();        

        // Inicializando swarm
        System.out.println("Inicializando o enxame...");

        this.initializeSwarm();

        if (plotManager.isDebug()) {
            System.out.println("Enxame:");
            plotManager.printParticles_CSS(swarm, problem);
        }
    }

    public void run() {

        // Calculando fitness das partículas*as


        System.out.println("Calculando fitness das partículas...");


        for (int i = 0; i < nParticles; i++) {

            swarm.get(i).calculateFitness();
//            swarm.set(i, repositoryOperators.updatePbest(swarm.get(i), problem));
        }

        if (plotManager.isDebug()) {
            System.out.println("Enxame:");
            plotManager.printParticles_CSS(swarm, problem);
        }

        System.out.println("Inicializando líderes...");
        externalArchive = repositoryOperators.initializeExternalArchive_CSS(externalArchive, swarm, problem);

        if (plotManager.isDebug()) {
            System.out.println("Tamanho do arquivo externo: " + getExternalArchive().size());

            System.out.println("Arquivo externo:");
            plotManager.printParticles_CSS(getExternalArchive(), problem);
        }

        System.out.println("Qualificando líderes por crowding distance...");
        externalArchive = repositoryOperators.qualityExternalArchive_CSS(externalArchive, problem);

        if (plotManager.isDebug()) {
            System.out.println("Arquivo externo ordenado:");
            plotManager.printParticles_CSS(externalArchive, problem);
        }

        this.currentNCallObjectiveFunction = 0;
        this.currentIteration = 0;

        while (currentNCallObjectiveFunction < Constants.NCALLOBJECTIVEFUNCTION) {
            if (plotManager.isDebug()) {
                System.out.println("\nIteracao " + (currentIteration + 1) + "\n");

                System.out.println("Operando sobre as partículas...");
            }

//            swarm = turbulenceOperators.turbulence(swarm, Constants.MUTATIONPERCENTUALPARTICLES, Constants.MUTATIONPERCENTUALDIMENSIONS, standardDeviationMutationGaussian, Constants.INITIALSDMUTATIONGAUSSIAN, Constants.FINALSDMUTATIONGAUSSIAN, nIterations, problem);

            if (plotManager.isDebug()) {
                System.out.println("Escolhendo o lider social 2...");
            }

            swarm = repositoryOperators.selectGBest2(swarm, externalArchive, problem);


            this.datumPoint = repositoryOperators.calculateDatumPoint(externalArchive, problem);

            for (int j = 0; j < nParticles; j++) {
                Particle_CSS particle_CSS = swarm.get(j);

                if (plotManager.isDebug()) {
                    System.out.println("Escolhendo o lider social 1...");
                }

                particle_CSS = repositoryOperators.selectGBest1(particle_CSS, externalArchive, datumPoint, problem);

                if (plotManager.isDebug()) {
                    System.out.println("Atualizando a posição da particula...");
                }

                particle_CSS = psoOperators.updatePosition(particle_CSS, problem, inertia, Constants.COGNITIONLEARNINGRATE, Constants.SOCIALLEARNINGRATE);


                if (plotManager.isDebug()) {
                    System.out.println("Calculando fitness da particula...");
                }

                particle_CSS.calculateFitness();
                currentNCallObjectiveFunction++;

                if ((currentNCallObjectiveFunction % 10000) == 9999) {                    
                    plotManager.plotExternalArchive_CSS(externalArchive, currentNCallObjectiveFunction + 1);
                    plotManager.plotSwarm_CSS(swarm, currentNCallObjectiveFunction + 1);
                }

                if (plotManager.isDebug()) {
                    System.out.println("Atualizando lista Pbest...");
                }
            }
            
            swarm = turbulenceOperators.turbulence_CSS( swarm, problem, Constants.STANDARDDEVIATIONMUTATIONGAUSSIAN );

            if (plotManager.isDebug()) {
                System.out.println("Adicionado novos líderes e removendo partículas dominadas do arquivo externo...");
            }

            externalArchive = repositoryOperators.updateExternalArchive_CSS(externalArchive, swarm, problem);
           
            if (plotManager.isDebug()) {
                System.out.println("Tamanho do arquivo externo: " + getExternalArchive().size());

                System.out.println("Qualificando líderes por crowding distance...");
            }

//            externalArchive = turbulenceOperators.localSearch(externalArchive, Constants.SIGMA, problem, currentNCallObjectiveFunction);
//            beforeNCallObjectiveFunction = currentNCallObjectiveFunction;
//
//            currentNCallObjectiveFunction = turbulenceOperators.getNCallObjectiveFunction();
//
//            if ((int) (beforeNCallObjectiveFunction / 10000) != (int) (currentNCallObjectiveFunction / 10000)) {
//                plotManager.plotExternalArchive(getExternalArchive(), ((int) (currentNCallObjectiveFunction / 10000)) * 10000);
//                plotManager.plotSwarm(swarm, ((int) (currentNCallObjectiveFunction / 10000)) * 10000);
//            }
            
            externalArchive = repositoryOperators.executeClusterMethod( externalArchive, problem );

            if (plotManager.isDebug()) {
                System.out.println("Tamanho do arquivo externo: " + externalArchive.size());

                System.out.println("Qualificando líderes por crowding distance...");
            }

            externalArchive = repositoryOperators.qualityExternalArchive_CSS(externalArchive, problem);

            if (plotManager.isDebug()) {
                System.out.println("Arquivo externo ordenado:");
                plotManager.printParticles_CSS(externalArchive, problem);

                System.out.println("Removendo partículas em excesso do arquivo externo...");
            }

            externalArchive = repositoryOperators.trunkExternalArchive_CSS(externalArchive, nMaxParticlesExternalArchive);


            if (externalArchive.size() > maxExternalArchive) {
                maxExternalArchive = getExternalArchive().size();
            }

            if (plotManager.isDebug()) {
                System.out.println("Adicionado novos líderes e removendo partículas dominadas do arquivo externo...");
                System.out.println("Tamanho do arquivo externo: " + getExternalArchive().size());
            }

            inertia = psoOperators.updateLinearInertia(Constants.MAXINERTIA, Constants.MININERTIA, currentNCallObjectiveFunction, Constants.NCALLOBJECTIVEFUNCTION);

//            inertia = inertia - ((maxInertia - minInertia) / nIterations);


//            System.out.println("Tamanho maximo atingido pela lista de Pbests na Iteracao " + i + ": "+ maxPbests );
//
//            maxPbests = 0;

//            this.plotExternalArchive( i+1 );

            currentIteration++;
        }
        
        elapsedTimeInMs = System.currentTimeMillis() - startTimeInMs;        

        plotManager.plotExternalArchive_CSS(externalArchive, currentNCallObjectiveFunction);
        plotManager.plotSwarm_CSS(swarm, currentNCallObjectiveFunction);
        
        plotManager.getJFreeChartExternalArchive().setTitle("CSS-MOPSO");        

        if (plotManager.isDebug()) {
            System.out.println("Soluções finais (arquivo externo):");
            plotManager.printParticles_CSS(externalArchive, problem);
        }

        System.out.println("Tamanho maximo atingido pela lista de Pbests: " + maxPbests);
        System.out.println("Tamanho maximo atingido pelo arquivo externo: " + maxExternalArchive);
        System.out.println("Tamanho final do arquivo externo: " + externalArchive.size());

        this.calculateMetrics();
    }

    void calculateMetrics() {
//        generationalDistance = perfomanceMetrics.calculateGenerationalDistance(swarm,getExternalArchive(), problem);
        hypervolume = perfomanceMetrics.calculateHypervolume2d_CSS(externalArchive, problem);
//        spread = perfomanceMetrics.calculateSpread(swarm,getExternalArchive(), problem);
        spacing = perfomanceMetrics.calculateSpacing_CSS(externalArchive, problem);
        maximumSpread = perfomanceMetrics.calculateMaximumSpread_CSS(externalArchive, problem);
        timeDuration = (double) (elapsedTimeInMs / 1000.0);                
//        gmean = perfomanceMetrics.calculateGMean(getExternalArchive());

//        System.out.println("Generational Distance: " + generationalDistance);
        System.out.println("Hypervolume: " + hypervolume);
//        System.out.println("Spread: " + spread);
        System.out.println("Spacing: " + spacing);
        System.out.println("Maximum Spread: " + maximumSpread);
        System.out.println("Time Duration (sec.): " + timeDuration);        

//        System.out.println("g() mean: " + gmean);
    }

    private void initializeSwarm() {
        for (int i = 0; i < nParticles; i++) {
            swarm.add(new Particle_CSS(problem, i));
        }
    }

    public ChartPanel getChartPanelExternalArchive() {
        return plotManager.getChartPanelExternalArchive();
    }

    public void setChartPanelExternalArchive(ChartPanel chartPanelExternalArchive) {
        plotManager.setChartPanelExternalArchive(chartPanelExternalArchive);
    }

    public ChartPanel getChartPanelSwarm() {
        return plotManager.getChartPanelSwarm();
    }

    public void setChartPanelSwarm(ChartPanel chartPanelSwarm) {
        plotManager.setChartPanelSwarm(chartPanelSwarm);
    }
    
    public NumberAxis getNumberAxisExternalArchive() {
        return plotManager.getNumberAxisExternalArchive();
    }     

    public ArrayList<Particle_CSS> getExternalArchive() {
        return externalArchive;
    }

    public void setExternalArchive(ArrayList<Particle_CSS> externalArchive) {
        this.externalArchive = externalArchive;
    }
    
    public MetricsReport getMetrics() {
        return new MetricsReport(hypervolume, spacing, maximumSpread, timeDuration);
    } 
}
